前面有提過,Ruby是一門相當徹底的物件導向語言,幾乎所有的東西都是物件,但block除外。要改變物件的狀態,在ruby最常見的方式就是.method,要拿這個method來用,就要從class內來存取,這個行為就是「存取控制」。
物件(object) = 狀態(state) + 行為(behavior)
來看最簡單的例子:
class Disney
def feel
puts "I feel very happy"
end
end
you = Disney.new
you.feel # => I feel very happy
you.feel這個東西看起來沒什麼特別,但在Ruby的解讀,會像有一個you物件,對它發送了一個「訊息」(message)feel,而這個you就是訊息的「接收者」(receiver)。
一般來說,public
的存取是Ruby已預設好的。就像上面例子,如果在方法上面沒有特別寫是哪種存取方式的話,那就是會是預設的public
。
用程式碼來看看三種控制:
class Disney
def feel
puts "I feel very happy"
end
private
def eat
puts "I wanna have some popcorn"
end
protected
def watch
puts "I wanna watch the show"
end
end
you = Disney.new
you.feel # => I feel very happy
you.eat # => NoMethodError
you.watch # => NoMethodError
另一種存取控制的寫法,寫在定義之後:
class Disney
def feel
puts "I feel very happy"
end
def eat
puts "I wanna have some popcorn"
end
def watch
puts "I wanna watch the show"
end
protected :eat
private :watch
end
you = Disney.new
you.feel # => I feel very happy
you.eat # => NoMethodError
you.watch # => NoMethodError
至於兩種寫法其實並沒有差別,我自己是比較偏向第一種方式,因為第二種感覺比較容易跟rails搞混,例如:
has_many :users
在各個程式語言都有存取控制的設計,private在其他語言被定義為「只有在類別內部才能被存取」,但在Ruby這裡的定義有點不太一樣:
不能在外部呼叫,也不能明確的指出「接收者」(receiver)
翻譯蒟蒻:若要呼叫private的方法時,前面不可以加小數點。
(備註:外部的定義不包含繼承的子類別)
上面例子因為eat方法是private method,所以向you(receiver)發送eat訊息(message)會出現NoMethodError。
如果定義上不能在receiver前加小數點,那我改成其他形式總行吧?
you = Disney.new
you.feel # => I feel very happy
you.send(:eat) # => I wanna have some popcorn
send是Ruby內建的方法,這邊只是把:eat當作參數送給you而已,指出接收者的是send方法而不是private method的eat,並沒有違反"不能明確的指出「接收者」的規定",所以這也是Ruby跟其他程式語言在private定義不同的地方。
class Disney
def feel
puts "I feel very happy"
end
private
def eat
puts "I wanna have some popcorn"
end
protected
def watch
puts "I wanna watch the show"
end
end
class Marvel < Disney
def marvel_watch
watch
end
def marvel_self_watch
self.watch
end
end
iron = Marvel.new
iron.marvel_watch # => I wanna watch the show
iron.marvel_self_watch # => I wanna watch the show
以繼承的類別Marvel來看,在父類別的protected method,不管用的是watch
或self.watch
都可以順利印出來。
不能在外部呼叫,但可以明確的指出「接收者」(receiver)
那如果改用private method呢?
class Disney
def feel
puts "I feel very happy"
end
private
def eat
puts "I wanna have some popcorn"
end
protected
def watch
puts "I wanna watch the show"
end
end
class Marvel < Disney
def marvel_eat
eat
end
def marvel_self_eat
self.eat
end
end
iron = Marvel.new
iron.marvel_eat # => I wanna have some popcorn
iron.marvel_self_eat # => NoMethodError
最後一行會印出錯誤,是因為private method不能明確的指出「接收者」。
參考資料:
Ruby Access Control Basics: Public vs Private vs Protected methods
Ruby Access Control
類別(Class)與模組(Module)
Ruby三種存取限制: Public, Protected, Private
Ruby: public, protected 與 private
“Success is falling nine times and getting up ten.”
— Jon Bon Jovi, Musician
本文同步發佈於: https://louiswuyj.tw/